package edu.asu.sefcom.extract.profile.data;

import java.io.FileInputStream;
import java.io.IOException;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;

import org.apache.log4j.Logger;
import org.jsoup.Jsoup;
import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.Transaction;
import org.neo4j.kernel.EmbeddedGraphDatabase;

public class Neo4jConnector implements DataConnector{

	public enum MyRelationshipTypes implements
			org.neo4j.graphdb.RelationshipType {
		Posts, Follows, FollowsBack, MemberOf
	}

	Logger logger = Logger.getLogger(Neo4jConnector.class);

	GraphDatabaseService graphDB;

	public Neo4jConnector() {
		Properties properties = new Properties();
		try {
			properties.load(new FileInputStream("config/app.properties"));
		} catch (IOException e) {
		}
		String dbDir = ""
				+ properties
						.getProperty("edu.asu.sefcom.extract.profile.database.dir");
		graphDB = new EmbeddedGraphDatabase(dbDir);
	}

	public Neo4jConnector(Object abc) {
	}

	public void shutdownDB() {
		graphDB.shutdown();
	}

	private Object translate(String article) throws Exception {
		String ret = "";
		try {
			ret = GoogleTranslate.translate(article);
		} catch (Exception e) {
			if (e.getLocalizedMessage().contains("404")) {
				logger.warn("404 thrown...");
				Thread.currentThread().sleep(3000);
			} else {
				Thread.currentThread().sleep(1000);
			}
			System.exit(0);
		}

		return ret;
	}

	private Object removeHtml(String article) {
		String ret = article.replaceAll("<br>", "");
		ret = ret.replaceAll("\\<.*?>", "");
		ret = ret.replaceAll("\\.*?>", "");
		return Jsoup.parse(ret).text();
	}

	public static long getNodebyName(GraphDatabaseService graphDB, String UName) {
//		logger.warn("in node by name "+ UName);
		Transaction tx = graphDB.beginTx();
		Node resultNode = null;
		try {
			Iterable<Node> Nodes = graphDB.getAllNodes();

			Iterator itr = graphDB.getAllNodes().iterator();

			while (itr.hasNext()) {
				Node node = (Node) itr.next();
				Iterator<String> keysItr = node.getPropertyKeys().iterator();
				while (keysItr.hasNext()) {
					String key = keysItr.next();
					if (key.equals("Name")) {
						if ((node.getProperty("Name")).equals(UName)) {
							resultNode = node;
						}
					}
				}
			}
			tx.success();
		} finally {
			tx.finish();
		}
		if (resultNode == null){
//			Neo4jConnector nc = new Neo4jConnector(null);
//			nc.graphDB=graphDB;
//			Node n=nc.createUserNode(UName, null, null);
//			return n.getId();}
			return 0;
		}
		else{
			return resultNode.getId();}

	}

	public Node createUserNode(String name, String created, String updated) {
		Node userNode = null;
//		String name1 = name.length() > 17 ? name.substring(0, 17) : name;
//		name = Jsoup.parse(name).text();
//		if (removenoise(name)) {
//			return null;
//		}
		long ID = 0;
		try {
			ID = getNodebyName(this.graphDB, name);
			userNode=this.graphDB.getNodeById(ID);
		} catch (Exception e) {
			e.printStackTrace();
		}

		if (ID == 0) {
			Transaction tx = graphDB.beginTx();
			try {
				userNode = graphDB.createNode();
				userNode.setProperty("Type", "User");
				userNode.setProperty("Name", name);
				userNode.setProperty("ICQ_UIN", "Empty");
				userNode.setProperty("Email", "Empty");
				userNode.setProperty("Follows", "Empty");
				userNode.setProperty("FollowsBack", "Empty");
				if (null != created) {
					Date cre = TranslateUtils.getDate(created);
					Date up = TranslateUtils.getDate(updated);
					userNode.setProperty("Created", cre);
					userNode.setProperty("Updated", up);
				}
				tx.success();
			} finally {
				tx.finish();
			}
			System.out.println("Created Node for User:"+name+" "+ getNodebyName(graphDB, name));
		}

		return userNode;
	}

	private boolean removenoise(String name) {
		boolean test = (name.contains("comment") || name.startsWith("comment")
				|| name.startsWith("Comment") || name.startsWith("Current"));
		return test;
	}

	public void storeUserCommData(List<String> dates, List<String> groups,
			String userName) {
//		long userNodeID = getNodebyName(graphDB, userName);
		long userNodeID = createUserNode(userName, null, null).getId();
		logger.warn("userNodeID : " + userNodeID +" name "+userName+ " commName- : " + groups);
		createCommunity(graphDB, null, groups, userNodeID);
		updateUser(userNodeID, dates);
	}

	private void updateUser(long userNodeID, List<String> dates) {
		Node userNode = graphDB.getNodeById(userNodeID);
		Date cre = null, up = null;
		try {
			cre = TranslateUtils.getDate(dates.get(0));
			up = TranslateUtils.getDate(dates.get(1));

		} catch (Exception e) {
		}

		Transaction tx = graphDB.beginTx();
		try {
			if (cre == null) {
				userNode.setProperty("Created", cre);
			}
			if (up == null) {
				userNode.setProperty("Updated", up);
			}
			tx.success();
		} finally {
			tx.finish();
		}
	}

	public void storeCommData(List<String> dates, List<String> users,
			String commName) {
		logger.warn("userNames- : " + users);
		long commNodeID = getNodebyName(graphDB, "" + removeHtml(commName));
		Node commNode = graphDB.getNodeById(commNodeID);
		for (String name : users) {
			Node userNode = createUserNode(name, null, null);
			if (userNode == null) {
				continue;
			}
			
			Transaction tx = graphDB.beginTx();
			try{
				userNode.createRelationshipTo(commNode,
						MyRelationshipTypes.MemberOf);
				tx.success();
			} finally {
				tx.finish();
			}
			
		}
		updateComm(commNode, dates);
	}

	private void updateComm(Node community, List<String> dates) {
		Transaction tx = graphDB.beginTx();
		Date cre = TranslateUtils.getDate(dates.get(0));
		Date up = TranslateUtils.getDate(dates.get(1));
		try {
			community.setProperty("Created", cre.toString());
			community.setProperty("Updated", up.toString());
			tx.success();
		} finally {
			tx.finish();
		}

	}

	private void createCommunity(GraphDatabaseService graphDB,
			List<String> dates, List<String> groups, long userNodeID) {
		Node community;
		Transaction tx = graphDB.beginTx();

		try {
			for (String name : groups) {
				if (!checkIfExistsComm(name)) {
					community = graphDB.createNode();
					community.setProperty("Type", "Comm");
					community.setProperty("Name", name);
					if (null != dates) {
						Date cre = TranslateUtils.getDate(dates.get(0));
						Date up = TranslateUtils.getDate(dates.get(1));
						community.setProperty("Created", cre.toString());
						community.setProperty("Updated", up.toString());
					}
					System.out.println("Create Node for Comm:" + name);
					Node user = graphDB.getNodeById(userNodeID);
					user.createRelationshipTo(community,
							MyRelationshipTypes.MemberOf);
					tx.success();
				}

			}
		} finally {
			tx.finish();
		}

	}

	public void storeFollowsData(String username, List<String> follows) {
		logger.warn("follows userNames- : " + follows);
		Node userNode= createUserNode(username,null,null);
//		Node userNode = graphDB.getNodeById(userNodeID);
		for (String name : follows) {
			Node followsNode = createUserNode(name, null, null);
			if (followsNode == null) {
				continue;
			}
			
			Transaction tx = graphDB.beginTx();
			try{
				userNode.createRelationshipTo(followsNode,
						MyRelationshipTypes.Follows);
				tx.success();
			}finally{
				tx.finish();
			}
		}
	}

	private void storeFollows(GraphDatabaseService graphDB, List<String> dates,
			List<String> groups, long userNodeID) {
		Node community;
		Transaction tx = graphDB.beginTx();

		try {
			for (String name : groups) {
				if (!checkIfExistsComm(name)) {
					community = graphDB.createNode();
					community.setProperty("Type", "Comm");
					community.setProperty("Name", name);
					if (null != dates) {
						Date cre = TranslateUtils.getDate(dates.get(0));
						Date up = TranslateUtils.getDate(dates.get(1));
						community.setProperty("Created", cre.toString());
						community.setProperty("Updated", up.toString());
					}
					System.out.println("Create Node for Comm:" + name);
					Node user = graphDB.getNodeById(userNodeID);
					user.createRelationshipTo(community,
							MyRelationshipTypes.MemberOf);
					tx.success();
				}

			}
		} finally {
			tx.finish();
		}

	}

	private boolean checkIfExistsComm(String name) {
		long ID = getNodebyName(this.graphDB, name);
		return ID == 0 ? false : true;
	}

}
